﻿package zf.data.geom.plan
{
	import flash.display.Sprite;
	import flash.geom.Point;

	import zf.data.geom.Beeline;
	import zf.data.geom.Formula;
	import zf.data.geom.ILine;

	/**
	 * 椭圆类，你可以使用静态方法Ellipse.creatEllipse()创建一个已知中心点和x,y轴半径的椭圆。
	 * 椭圆标准方程：x^2/a^2 + y^2/b^2 = 1    
	 * 椭圆一般方程：A x^2 + B x y + C y^2 + D x + E y + F = 0;
	 * 椭圆一般方程(A是长轴与x轴夹角，dx和dy是中心点坐标)
	 * [(x-dx) cosA + (y-dy) sinA]^2 / a^2 + [(y-dy) cosA - (x-dx) sinA]^2] / b^2 = 1
	 * [x cosA + y sinA]^2 / a^2 + [(y-dy) cosA - (x-dx) sinA]^2] / b^2 = 1
	 * [U*COS(W) - V*SIN(W) + S]^2/A^2 + [U*SIN(W) + V*COS(W) + T]/B^2 = 1
	 * [x cosA - y sinA + dx]^2 / a^2 + [y cosA + x sinA + dy]^2] / b^2 = 1
	 * 旋转公式 
	 * x'=x*cos(n)+y*sin(n) 
	 * y'=-x*sin(n)+y*cos(n) 
	 * (x,y)表示原来的坐标，(x',y')表示旋转后的坐标，n是逆时针转角
	 * 
	 * @author Mike.zhang
	 * @langversion 3.0 
	 * @playerversion Flash 9 
	 */
	public class Ellipse
	{
		private var _a:Number;
		private var _b:Number;
		private var _center:Point;
		private var _radian:Number;

		/**
		 * 创建一个椭圆 
		 * 
		 */
		public function Ellipse ()
		{
			super ();
		}

		/**
		 * 椭圆与x轴正方向所成的角度，即 长(短)轴与x轴的角度，单位弧度。
		 * @return 弧度
		 * 
		 */
		public function get radian ():Number
		{
			return _radian;
		}

		public function set radian (v:Number):void
		{
			_radian=v;
		}
		/**
		 * x轴半径 
		 * @return 
		 * 
		 */
		public function get a ():Number
		{
			return _a;
		}
		public function set a (v:Number):void
		{
			_a=v;
		}

		/**
		 * y轴半径 
		 * @return 
		 * 
		 */
		public function get b ():Number
		{
			return _b;
		}

		public function set b (v:Number):void
		{
			_b=v;
		}

		/**
		 * 椭圆中心点 
		 * @return 
		 * 
		 */
		public function get center ():Point
		{
			return _center;
		}

		public function set center (v:Point):void
		{
			_center=v;
		}

		/**
		 * 绘制椭圆
		 * 在使用前需要设置填充及线条信息。 
		 * 
		 * 
		 * @inheritDoc 
		 * 
		 */
		/**
		 * 绘制椭圆
		 * 在使用前需要设置填充及线条信息。 
		 * @param target 一个可以绘制的对象。将在这个对象上绘制图形。请注意，它必须有graphics，一般情况下，它必须是flash.display.Sprite类及其子类
		 * @param steps 步骤数，绘制椭圆的点的个数，数字越大绘制的椭圆越精确
		 */
		public function draw (target:Sprite,steps:uint=100):void
		{
			if (radian == 0)
			{
				target.graphics.drawEllipse (center.x - a,center.y - b,2 * a,2 * b);
			}
			else
			{
				var points:Array=new Array  ;

				var beta:Number=radian;
				var sinbeta:Number=Math.sin(beta);
				var cosbeta:Number=Math.cos(beta);

				var alpha:Number;
				var sinalpha:Number;
				var cosalpha:Number;
				var X:Number;
				var Y:Number;

				for (var i:Number=0; i < 360; i+= 360 / steps)
				{
					alpha=i * Math.PI / 180;
					sinalpha=Math.sin(alpha);
					cosalpha=Math.cos(alpha);

					X=center.x + a * cosalpha * cosbeta - b * sinalpha * sinbeta;
					Y=center.y + a * cosalpha * sinbeta + b * sinalpha * cosbeta;

					points.push (new Point(X,Y));
				}
				target.graphics.moveTo (points[0].x,points[0].y);
				for (i=0; i < steps; i++)
				{
					target.graphics.lineTo (points[i].x,points[i].y);
				}
				target.graphics.lineTo (points[0].x,points[0].y);
			}
		}
		/**
		 * 依据中心点和a,b属性创建椭圆 
		 * @param center 椭圆中心点
		 * @param a 椭圆x半径
		 * @param b 椭圆y半径
		 * @param angle 椭圆旋转度，单位是度
		 * @return 椭圆
		 * 
		 */
		public static  function creatEllipse (center:Point,a:Number,b:Number,angle:Number=0):Ellipse
		{
			var myellipse:Ellipse=new Ellipse  ;
			myellipse.center=center;
			myellipse.a=a;
			myellipse.b=b;
			myellipse.radian=Formula.angleToRadian(angle);
			return myellipse;
		}
		/**
		 * 检测点p是否在椭圆上(包括椭圆边),请注意，由于会有小数或无理数该方法的计算将有些误差 
		 * @param p 待测的点
		 * @return 在椭圆上返回true
		 * 
		 */
		public function hitPoint (p:Point):Boolean
		{

			//公式：[(x-dx) cosA + (y-dy) sinA]^2 / a^2 + [(y-dy) cosA - (x-dx) sinA]^2] / b^2 = 1
			var po:Point=Formula.transformPoint(p,radian,center);
			//var f:Number=Math.cos(radian);
			//var e:Number=Math.sin(radian);
			//return Math.pow((p.x-center.x)*f+(p.y-center.y)*e,2)/(a*a)+Math.pow((p.y-center.y)*f-(p.x-center.x)*e,2)/(b*b)<=1;
			return po.x * po.x / a * a + po.y * po.y / b * b <= 1;
		}
		/**
		 * 检测椭圆与直线的相交情况，如果有交点，会有1到2个交点，返回的数组每个为一个Point的交点，如果没有交点返回null。请注意，由于会有小数或无理数该方法的计算将有些误差 
		 * @param l 待检测的直线
		 * @return 如果有交点，会有1到2个交点，返回的数组每个为一个Point的交点，如果没有交点返回null。
		 * see Beeline
		 */
		/*public function hitLine(l:Beeline):Array
		{
		//坐标系转换法
		//var bl:Beeline=l.turn(radian,center);
		var p1:Point=Formula.transformPoint(new Point(0,l.b),radian,center);
		var p2:Point=Formula.transformPoint(new Point(-l.b/l.k,0),radian,center);
		var bl:Beeline=Beeline.PtoBeeline(p1,p2);
		trace(l);
		trace(bl.turn(radian,center));
		
		var A:Number=b*b+a*a*bl.k*bl.k;
		var B:Number=2*a*a*bl.k*bl.b;
		var C:Number=a*a*(bl.b*bl.b-b*b);
		
		var num:Number=B*B-4*A*C;
		if(num>=0)
		{
		var arr:Array=new Array;
		var x1:Number,y1:Number;
		//var pc:Point=new Point(-center.x,-center.y);
		if(num==0)
		{
		x1=-B*0.5/A;
		y1=bl.k*x1+bl.b;
		p1=Formula.obTransformPoint(new Point(x1,y1),radian,center);
		arr.push(p1);
		return arr;
		}else
		{
		var x2:Number,y2:Number;
		num=Math.sqrt(num);
		x1=(-B+num)*0.5/A;
		y1=bl.k*x1+bl.b;
		x2=(-B-num)*0.5/A;
		y2=bl.k*x2+bl.b;
		p1=Formula.obTransformPoint(new Point(x1,y1),radian,center);
		p2=Formula.obTransformPoint(new Point(x2,y2),radian,center);
		arr.push(p1);
		arr.push(p2);
		return arr;
		}
		}
		return null;
		}*/

		public function hitLine (l:ILine):Array
		{
			//代入法
			var e:Number=Math.sin(radian);
			var f:Number=Math.cos(radian);
			var m:Number=center.x;
			var n:Number=center.y;
			var k:Number=l.k;
			var d:Number=l.b;

			var num1:Number=d * f - n * f + m * e;
			var num2:Number=d * e - n * e - m * f;

			var A:Number=Math.pow(a * k * f - e,2) + Math.pow(b * f + k * e,2);
			var B:Number=2 * b * b * f + k * e * num2 + 2 * a * a * k * f - e * num1;
			var C:Number=Math.pow(a * num1,2) + Math.pow(b * num2,2) - a * a * b * b;

			//var A:Number=Math.pow((k*f-e)/b,2)+Math.pow((f+k*e)/a,2);
			//var B:Number=2*(f+k*e)*num2/(a*a)+2*(k*f-e)*num1/(b*b);
			//var C:Number=Math.pow(num1/b,2)+Math.pow(num2/a,2)-1;


			var num:Number=B * B - 4 * A * C;
			if (num >= 0)
			{
				var arr:Array=new Array  ;
				var x1:Number,y1:Number;
				if (num == 0)
				{
					x1=- B * 0.5 / A;
					y1=l.k * x1 + l.b;
					arr.push (new Point(x1,y1));
					return arr;
				}
				else
				{
					var x2:Number,y2:Number;
					num=Math.sqrt(num);
					x1=- B + num * 0.5 / A;
					y1=l.k * x1 + l.b;
					x2=- B - num * 0.5 / A;
					y2=l.k * x2 + l.b;
					arr.push (new Point(x1,y1));
					arr.push (new Point(x2,y2));
					return arr;
				}
			}
			return null;
		}
		/**
		 * 返回椭圆的描述信息 
		 * @return 格式[center=(dx,dy) radiiA=a radiiB=b]
		 * 
		 */
		public function toString ():String
		{
			return "[center=" + center + " radiiA=" + a + " radiiB=" + b + "]";
		}
	}
}